use std::time::{self, Duration};

use log::info;
use poem::{listener::TcpListener, Route, Server};
use poem_openapi::{param::Query, payload::PlainText, OpenApi, OpenApiService};

use servicecomb::model::sc::{AddServiceInfo, InstanceStatus, ServiceSchemaInfo};
use servicecomb::registry::sc::{ServiceCenter, ServiceCenterInner, ServiceCenterOption};
use servicecomb::Result;

struct Api;

#[OpenApi()]
impl Api {
    #[oai(path = "/api/hello", method = "get", operation_id = "hello")]
    async fn index(&self, name: Query<Option<String>>) -> PlainText<String> {
        info!("query name {:?}", name.0);
        match name.0 {
            Some(name) => PlainText(format!("hello: {name}!")),
            None => PlainText("hello!".to_string()),
        }
    }
}

async fn test_api1() -> Result<()> {
    let var_name = OpenApiService::new(Api, "Hello World", "1.0");
    let api_service = var_name.server("http://0.0.0.0:3000/api");
    let spec = api_service.spec_yaml();
    println!("spec: {}", spec);
    let ui = api_service.swagger_ui();
    let var_name = Server::new(TcpListener::bind("0.0.0.0:3000"));

    let opt = ServiceCenterOption {
        sc_addr: "127.0.0.1:30100".to_owned(),
        host_name: "default".to_owned(),
        addr: "127.0.0.1:3000".to_owned(),
        domain: "default".to_owned(),
        app_id: "eIoT".to_owned(),
        service_name: "rust-demo".to_owned(),
        version: "1.0.3".to_owned(),
    };
    let sc = ServiceCenter::start(opt).await?;
    tokio::time::sleep(Duration::from_secs(5)).await;

    let api = ServiceSchemaInfo {
        schema_id: "api".to_owned(),
        schema: spec,
        summary: "api".to_owned(),
    };
    sc.regist_service(AddServiceInfo {
        service_name: "rust-demo".to_owned(),
        service_version: "1.0.1".to_owned(),
        addr: "127.0.0.1:3000".to_owned(),
        schemes: vec![api],
    })
    .await?;

    tokio::spawn(async {
        let _ = var_name
            .run(Route::new().nest("/", api_service).nest("/ui", ui))
            .await;
    });
    let st = time::Instant::now();
    let info = sc.find_service("rust-demo".to_owned()).await?;
    println!("rust-demo: {:?}", info);
    println!("best ep url: {:?}", info.get_best_addr());
    const NUM: usize = 200000;
    for i in 0..NUM {
        let result = ureq::get(format!(
            "{}/api/hello?name={}",
            info.get_best_addr().unwrap(),
            i
        ))
        .call()
        .unwrap()
        .body_mut()
        .read_to_string()
        .unwrap();
        // println!("result: {}", result);
        // tokio::time::sleep(Duration::from_millis(10)).await;
        if (i + 1) % 1000 == 0 {
            println!("{}%", (i + 1) * 100 / NUM);
        }
    }
    let spent = st.elapsed();
    println!(
        "{} elapsed: {:?}, speed {} qps",
        NUM,
        spent,
        NUM as u64 * 1000 / spent.as_millis() as u64
    );
    drop(sc);
    tokio::time::sleep(Duration::from_secs(3)).await;
    Ok(())
}

async fn test_api() -> Result<()> {
    let var_name = OpenApiService::new(Api, "Hello World", "1.0");
    let api_service = var_name.server("http://0.0.0.0:3000/api");
    let spec = api_service.spec_yaml();
    println!("spec: {}", spec);
    let ui = api_service.swagger_ui();
    let var_name = Server::new(TcpListener::bind("0.0.0.0:3000"));

    let opt = ServiceCenterOption {
        sc_addr: "127.0.0.1:30100".to_owned(),
        host_name: "default".to_owned(),
        addr: "127.0.0.1:3000".to_owned(),
        domain: "default".to_owned(),
        app_id: "eIoT".to_owned(),
        service_name: "rust-demo".to_owned(),
        version: "1.0.0".to_owned(),
    };
    let sc = ServiceCenterInner::new(opt);
    let service_id = sc.regist().await?;
    println!("service_id {}", service_id);

    let instance_id = sc.regist_instance(&service_id).await?;
    println!("instance id {}", instance_id);
    let gslb_instances = sc
        .find_service_instance(service_id.as_str(), "gslb")
        .await?;
    println!("gslb: {:?}", gslb_instances);
    let instances = sc
        .find_service_instance(service_id.as_str(), "rust-demo")
        .await?;
    println!("rust-demo: {:?}", instances);
    let api = ServiceSchemaInfo {
        schema_id: "api".to_owned(),
        schema: spec,
        summary: "api".to_owned(),
    };
    sc.regist_schema(service_id.as_str(), &vec![api]).await?;
    sc.heartbeat(&service_id, &instance_id).await?;
    sc.update_status(&service_id, &instance_id, InstanceStatus::Up)
        .await?;
    let _ = var_name
        .run(Route::new().nest("/api", api_service).nest("/", ui))
        .await;
    Ok(())
}

#[tokio::main]
async fn main() {
    env_logger::init();
    // test_api().await.expect("test failed");
    test_api1().await.expect("test1 failed");
}
